#include <iostream>
#include <fstream>
// #include <sstream>
#include <vector>
#include <ctime>
#ifdef _WIN32
#include <windows.h>
#endif
#include "json.hpp"
using namespace std;
using namespace nlohmann;
#ifdef _WIN32
string utf8_conv(string raw) {
	const char* utf8 = raw.c_str();
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1];
	memset(wstr, 0, len + 1);
	MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len + 1];
	memset(str, 0, len + 1);
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
	if (wstr) delete[] wstr;
	return str;
}
#endif
void speak(string text) {
	string cmd;
#if defined(_WIN32)
	cmd += "mshta vbscript:CreateObject(\"SAPI.SpVoice\").speak(\"";
	cmd += utf8_conv(text);
	cmd += "\")(Window.close) >nul 2>nul";
#elif defined(__APPLE__)
	cmd += "say \"";
	cmd += text;
	cmd += "\" >/dev/null 2>/dev/null";
#else
	return;
#endif
	system(cmd.c_str());
}
void start(string path) {
	string cmd;
#if defined(_WIN32)
	ShellExecute(0, "open", utf8_conv(path).c_str(), "", "", SW_SHOWNORMAL);
	return;
#elif defined(__APPLE__)
	cmd += "open \"";
	cmd += path;
	cmd += "\" >/dev/null 2>/dev/null";
#elif defined(__linux__)
	cmd += "xdg-open \"";
	cmd += path;
	cmd += "\" >/dev/null 2>/dev/null";
#else
	return;
#endif
	system(cmd.c_str());
}
int main() {
	srand(time(0));
#ifdef _WIN32
	// system("chcp 65001 >nul");
	// int cp = GetConsoleOutputCP();
	SetConsoleOutputCP(65001);
#endif
	cout << "『さあ、弱者が見捨てられない楽園を築くのだ！』" << endl
	     << "↑ 这个是私货，不要在意。" << endl << endl
		 << "==========分割线==========" << endl << endl
		 << "201804 Lottery System ver0.1" << endl << endl;
	ifstream fin;
	fin.open("config.json");
	if (!fin.is_open()) {
		cerr << "Failed to open config.json. The program will exit.";
		return 1;
	}
	/*
	std::stringstream ss;
	ss << fin.rdbuf();
	json j = json::parse(ss.str());
	*/
	json j;
	try {
		fin >> j;
	}
	catch(...) {
		cerr << "There is a syntax error in the config file.";
		return 1;
	}
	if (!j["data"].is_array()) {
		cerr << "The 'data' key does not match the correct type. The program will exit.";
		return 1;
	}
	if (j["data"].size() < 1) {
		cerr << "There isn't any data yet. Please add data first.";
		return 1;
	}
	string temp, head = "", tail = "", res;
	int len = j["data"].size(), x = 0, first, last, rnd;
	while (x < len) {
		if (j["data"][x].is_array()) {
			if (j["data"][x][0].is_number() && j["data"][x][1].is_number()) {
				first = j["data"][x][0].get<int>();
				last = j["data"][x][1].get<int>();
				if (first < last) for (int y = first; y <= last; y++) j["data"].emplace_back(head + to_string(y) + tail);
				else              for (int y = first; y >= last; y--) j["data"].emplace_back(head + to_string(y) + tail);
			}
		}
		if (!j["data"][x].is_number() && !j["data"][x].is_string()) {
			j["data"].erase(x);
			len--;
		}
		else x++;
	}
	bool rep = j["repeat"].is_boolean() ? j["repeat"].get<bool>() : false;
	bool spk = j["speak"].is_boolean() ? j["speak"].get<bool>() : true;
	if (j["format"][0].is_string() && j["format"][1].is_string()) {
		head = j["format"][0].get<string>();
		tail = j["format"][1].get<string>();
	}
	while (true) {
		rnd = rand() % j["data"].size();
		if (j["data"][rnd].is_number()) res = to_string(j["data"][rnd].get<int>());
		if (j["data"][rnd].is_string()) res = j["data"][rnd].get<string>();
		res = head + res + tail;
		cout << res << endl;
		if (spk) speak(res);
		if (!rep) j["data"].erase(rnd);
		if (j["data"].size() < 1) {
			cout << "All elements have been extracted. Press Enter to exit the program.";
			if (j["egg"].is_string()) start(j["egg"].get<string>());
			if (j["egg"].is_array()) for (auto& z : j["egg"].items()) if (z.value().is_string()) start(z.value().get<string>());
			getchar();
			return 0;
		}
		else {
			cout << "Press Enter to extract the next element...";
			getchar();
		}
	}
    return 0;
}